/*
 * Copyright (C) 2015 Daniel Uppenkamp
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.googlecode.maurersmarbles.states;

import com.google.common.base.Preconditions;
import com.googlecode.maurersmarbles.Card;
import com.googlecode.maurersmarbles.CardDeck;
import com.googlecode.maurersmarbles.GameLogic;
import com.googlecode.maurersmarbles.GameState;
import com.googlecode.maurersmarbles.InputEvent;
import com.googlecode.maurersmarbles.Rectangle;
import com.googlecode.maurersmarbles.Screen;
import com.googlecode.maurersmarbles.drawables.CardDrawable;
import com.googlecode.maurersmarbles.drawables.Drawable;
import java.util.Map;

/**
 *
 * @author Daniel Uppenkamp
 */
public class DealCardsState extends TableState {

    /**
     * The DealCardsState instance.
     */
    private static final DealCardsState INSTANCE = new DealCardsState();
    /**
     * The animation time (in millis) to deal a card.
     */
    protected static final long ANIMATION_TIME_DEAL_CARD = 250L;
    /**
     * The status message to display when dealing cards.
     */
    protected static final String STATUS_DEALING_CARDS = "Dealing Cards";

    /**
     * Creates a new DealCardsState.
     */
    private DealCardsState() {
        // do nothing
    }

    /**
     * Animates the given card being dealt to the given player.
     *
     * @param screen the screen
     * @param gameState the game state
     * @param card the card to deal
     * @param playerNumber the player number
     * @param duration the duration of the animation (in millis)
     */
    protected void animateCardBeingDelt(Screen screen, GameState gameState, Card card, int playerNumber, long duration) {

        Preconditions.checkNotNull(screen, "Null screen");
        Preconditions.checkNotNull(card, "Null card");
        Preconditions.checkNotNull(boardDrawable, "Null board drawable");

        Long currentTime;
        Long startTime = currentTime = System.currentTimeMillis();

        while (currentTime < (startTime + duration)) {

            Map<Object, Drawable> drawables = getDrawables(screen, gameState);

            Rectangle cardDeckRectangle = boardDrawable.getRectangleForCardDeck();
            Rectangle playerCardRectangle = getRectangleForPlayerCard(screen, gameState, playerNumber, null);

            if (cardDeckRectangle != null) {

                double weight = (double) (currentTime - startTime) / (double) duration;

                Rectangle rectangle = Rectangle.interpolate(cardDeckRectangle, playerCardRectangle, weight);
                drawables.put(card, new CardDrawable(null, rectangle));

                screen.draw(drawables.values());

            }

            currentTime = System.currentTimeMillis();

        }

    }

    /**
     * Returns the DealCardsState instance.
     *
     * @return the DealCardsState instance
     */
    public static DealCardsState getInstance() {
        return INSTANCE;
    }

    @Override
    public State enter(Screen screen, GameState gameState) {

        Preconditions.checkNotNull(gameState, "Null game state");

        // advance the dealer
        gameState.setDealer(GameLogic.getNextPlayerNumber(gameState.getDealer()));

        // get the card deck
        CardDeck cardDeck = gameState.getCardDeck();
        if (cardDeck == null || cardDeck.isEmpty()) {

            // TODO Add a shuffle animation?
            // the card deck is not set or empty, create a new shuffled deck
            cardDeck = new CardDeck(true);
            gameState.setCardDeck(cardDeck);

            // clear the discard pile
            discardedCard = null;

        }

        // get the number of cards to deal to each player
        int numberOfCardsPerPlayer = GameLogic.getNumberOfCardsToDealPerPlayer(gameState);

        // deal the cards to each player
        for (int i = 0; i < numberOfCardsPerPlayer; i++) {

            for (int j = 1; j <= GameLogic.NUMBER_OF_PLAYERS; j++) {

                int playerNumber = gameState.getDealer() + j;
                if (playerNumber >= GameLogic.NUMBER_OF_PLAYERS) {
                    playerNumber -= GameLogic.NUMBER_OF_PLAYERS;
                }

                Card card = cardDeck.nextCard();

                if (screen != null) {
                    animateCardBeingDelt(screen, gameState, card, playerNumber, ANIMATION_TIME_DEAL_CARD);
                }

                gameState.getPlayers().get(playerNumber).getCards().add(card);

            }

        }

        if (screen != null) {
            screen.draw(getDrawables(screen, gameState).values());
        }

        return PlayerTurnState.getInstance(GameLogic.getNextPlayerNumber(gameState.getDealer()));

    }

    @Override
    protected Map<Object, Drawable> getDrawables(Screen screen, GameState gameState) {

        Preconditions.checkNotNull(gameState, "Null game state");

        // set the status in the game state
        gameState.setStatus(STATUS_DEALING_CARDS);

        return super.getDrawables(screen, gameState);

    }

    @Override
    public State handleInputEvent(Screen screen, GameState gameState, InputEvent inputEvent) {
        // do nothing and return null to stay in this state
        return null;
    }

    @Override
    public void handleResizeEvent(Screen screen, GameState gameState, int width, int height) {
        /**
         * Do nothing. This state does all of the work in the enter function and
         * then moves to the next state. If a resize event happens, the screen
         * will be redrawn on the next animation.
         */
    }

}
